//********************************************************************
//	created:	15:10:2011   21:43
//	filename: 	asyncsocket.h
//	author:		tiamo
//	purpose:	socket
//********************************************************************

#pragma once

namespace network
{
	//
	// forward decl
	//
	class SocketManager;

	//
	// packet
	//
	class BasePacket : public utils::RefCountedObject
	{
	public:
		//
		// constructor
		//
		BasePacket(wchar_t const* name) : mName(name)						{}

		//
		// destructor
		//
		virtual ~BasePacket()												{}

		//
		// get name
		//
		wchar_t const* getName() const										{return mName;}

	protected:
		//
		// name
		//
		wchar_t const*														mName;
	};

	//
	// async socket
	//
	class AsyncSocket : public utils::RefCountedObject
	{
		friend class SocketManager;

	public:
		//
		// connect callback
		//
		typedef std::function<void(AsyncSocket*, bool)>						ConnectCallback;
		
		//
		// close callback
		//
		typedef std::function<void(AsyncSocket*, bool)>						CloseCallback;

		//
		// data packet callback
		//
		typedef std::function<void(AsyncSocket*, BasePacket*)>				DataPacketCallback;

		//
		// decode data callback
		//
		typedef std::function<utils::SmartPointer<BasePacket>(AsyncSocket*, void const*, uint32_t, uint32_t&)>	DecodeDataCallback;

	public:
		//
		// constructor
		//
		AsyncSocket(uint32_t recvBufferSize = 8192, uint32_t sendBufferSize = 8192);

		//
		// destructor
		//
		virtual ~AsyncSocket();

		//
		// create client socket
		//
		bool createTcpClientSocket(UniString const& hostName, uint16_t port, bool nonBlock);

		//
		// create tcp server socket
		//
		void createTcpServerSocket(UniString const& listenAddress, uint16_t listenPort, bool nonBlock);

		//
		// attach accepted socket
		//
		void attachAcceptedSocket(SOCKET handle, AddressHelper const& address, bool nonBlock);

		//
		// send buffer
		//
		uint32_t sendBuffer(void const* inputBuffer, uint32_t inputLength);

		//
		// set connect callback
		//
		void setConnectCallback(ConnectCallback&& connectCallback)			{mConnectCallback = std::move(connectCallback);}

		//
		// get connect callback
		//
		ConnectCallback& getConnectCallback()								{return mConnectCallback;}

		//
		// set close callback
		//
		void setCloseCallback(CloseCallback&& closeCallback)				{mCloseCallback = std::move(closeCallback);}

		//
		// get close callback
		//
		CloseCallback& getCloseCallback()									{return mCloseCallback;}

		//
		// set data packet callback
		//
		void setDataPacketCallback(DataPacketCallback&& dataPacketCallback)	{mDataPacketCallback = std::move(dataPacketCallback);}

		//
		// get data packet callback
		//
		DataPacketCallback& getDataPacketCallback()							{return mDataPacketCallback;}

		//
		// set decode data callback
		//
		void setDecodeDataCallback(DecodeDataCallback&& decodeDataCallback)	{mDecodeDataCallback = std::move(decodeDataCallback);}

		//
		// clear callback
		//
		void clearCallback()												{mConnectCallback = nullptr; mCloseCallback = nullptr; mDataPacketCallback = nullptr;}

		//
		// set user data
		//
		void setUserData(void* userData)									{mUserData = userData;}

		//
		// get user data
		//
		void* getUserData() const											{return mUserData;}

		//
		// get handle
		//
		SOCKET getHandle() const											{return mHandle;}

		//
		// get address
		//
		AddressHelper const& getAddress(bool isLocal) const					{return mAddress[isLocal ? 0 : 1];}

		//
		// get socket manager
		//
		SocketManager* getSocketManager() const								{return mSocketManager;}

		//
		// close after send
		//
		void closeAfterSend()												{mCloseAfterSend = true;}

		//
		// valid
		//
		bool isValid() const												{return mHandle != INVALID_SOCKET;}

		//
		// can send buffer
		//
		bool canSendBuffer() const											{return mCanSendBuffer;}

	private:
		//
		// write data to socket
		//
		uint32_t writeDataToSocket();

		//
		// read from socket
		//
		uint32_t readFromSocket();

		//
		// close
		//
		void close(bool errorState);

		//
		// set socket manager
		//
		void setSocketManager(SocketManager* socketManager)					{mSocketManager = socketManager;}

		//
		// flush write buffer
		//
		void flushWriteBuffer();

	protected:
		//
		// setup address
		//
		void setupAddress(AddressHelper const* localAddress, AddressHelper const* peerAddress);

		//
		// create tcp socket
		//
		void createTcpSocket(int familyType, bool ignoreError = false);

		//
		// bind address
		//
		void bindAddress(AddressHelper const& address);

		//
		// connect to remote
		//
		bool connect(AddressHelper const& peerAddress);

		//
		// connect to remote
		//
		bool connect(UniString const& hostName, uint16_t port, bool nonBlock);

		//
		// listen
		//
		void listen();

		//
		// accept
		//
		SOCKET accept(AddressHelper& address);

		//
		// set non-block
		//
		void setNonBlock(bool nonBlock);

		//
		// set reuse-address
		//
		void setReuseAddress(bool reuse);

		//
		// get socket error
		//
		int getSocketError();

		//
		// copy send buffer
		//
		uint32_t readSendBuffer(void* outputBuffer, uint32_t bufferLength);

		//
		// consume send buffer
		//
		void consumeSendBuffer(uint32_t bufferLength);

		//
		// is block error
		//
		virtual bool isWouldBlockError() const;

		//
		// network event
		//
		virtual void onNetworkEvent(bool readEvent, bool writeEvent, bool errorEvent);

		//
		// error
		//
		virtual void onError(std::exception* theException);

		//
		// connect
		//
		virtual void onConnected(bool errorState);

		//
		// closed
		//
		virtual void onSocketClosed(bool errorState);

		//
		// process recieved data
		//
		virtual bool processReceivedData();

	private:
		//
		// read
		//
		virtual int realRead(void* buffer, uint32_t bufferLength)			{return recv(mHandle, static_cast<char*>(buffer), bufferLength, 0);}

		//
		// write
		//
		virtual int realWrite(void const* buffer, uint32_t bufferLenth)		{return send(mHandle, static_cast<char const*>(buffer), bufferLenth, 0);}

	protected:
		//
		// socket manager
		//
		SocketManager*														mSocketManager;

		//
		// socket
		//
		SOCKET																mHandle;

		//
		// address
		//
		AddressHelper														mAddress[2];

		//
		// send buffer
		//
		utils::DataBuffer													mSendBuffer;

		//
		// recv buffer
		//
		utils::DataBuffer													mRecvBuffer;

		//
		// send buffer lock
		//
		utils::Mutex														mSendBufferLock;

		//
		// connected
		//
		bool																mConnected;

		//
		// can send buffer
		//
		bool																mCanSendBuffer;

		//
		// close after send
		//
		bool																mCloseAfterSend;

		//
		// connect callback
		//
		ConnectCallback														mConnectCallback;

		//
		// close callback
		//
		CloseCallback														mCloseCallback;

		//
		// data packet callback
		//
		DataPacketCallback													mDataPacketCallback;

		//
		// decode data callback
		//
		DecodeDataCallback													mDecodeDataCallback;

		//
		// user data
		//
		void*																mUserData;
	};
}
